Kotlin for Android, iOS and beyond
by taylorcyang
from KotlinConf 2018
K/N targets whatever LLVM targets!
Kotlin/Native is:
- Kotlin compiles to native targets(without vm)
- A target of Kotlin lang.
- with all kotlin features
- K/N itself is not multiplatform
- K/N makes Kotlin multiplatform possible.
K/N is still in beta
Solution | Type |
---|---|
|
Infrastructure |
|
Language + UI Binding |
Language + Framework |
share cpp code with Android and iOS
Pros:
- High performance
- compat executable
Cons:
- Hard to learn/write
- Android: requeire JNI
- iOS: requires Objective-C++ bridge
- manual memory manangement
- all other CPP disadvantage
Wechat | Tencent Drive
Translate Java code to OC
Pros:
- Java
Cons:
- ARC on Java (@Weak)
- large executable size (Java stdlib) 30+MB
Bundle an ART VM into iOS app
- Java
- Fully functional JVM
- GC
Cons:
- Extrimly large executable size(JVM and java stdlib) 80MB
PS: Xamarin is similar.
Language + Binding
Pros:
- JavaScript? (C#?)
- Front end community
Cons:
- Large executable size (V8)
- Performance
- Native UI knowledge
Language + Framework
Pros:
- Dart VM
- MethodChannel
- UI performance
Cons:
- binary size & RAM consumption
- goes pure dart?
- platform API call
- how long can it survive?
soultion | cons |
---|---|
C++ J2Objc Multi-OS Engine |
1. none-unified coding style 2. large stdlib(VM) to bundle |
R/N Weex Flutter | learn 3 platforms (2+1) |
ARC with cycle collector
automated reference counter with a cycle collector to collect cyclical garbage.
C/C++ | OC/Swift | Java | K/N |
manual | half-auto | auto | auto |
freeze()
marks an object tree inmmutable, thus sharing across threadsDetachedObjectGraph
to transfer ownership to other thread, thus mutable.Still under constructing...
coroutint isn't truly concurrent, yet
Kotlin on JVM platform
You know What is it.
K/N call OC methods
K/N extends OC class (and/or impl oc protocol)
passing K/N reference to OC
and vice versa
withiout performance penalty
kotlin
NSRunLoop.mainRunLoop().performBlock { block.run() }
swift
presenter = StoryContentPresenter(storyId: detailStory.id) presenter.onLoadData = { [unowned self] data in self.renderData(data) return KotlinUnit() }
OC
SharedCodeStoryContentPresenter *presenter = [[SharedCodeStoryContentPresenter: alloc] initWithStoryId: story.id]]
// create a kotlin instance let block = LinkedList.Companion().createACallable() block(KotlinInt.init(value: 42)) // and use it
All kotlin class inherits KotlinBase which inherits NSObject
let list = LinkedList() // kotlin class list.put(e: Junk()) // Junk is swift class
OC class must inhert from
NSObject
to pass to kotlin
@ExportObjCClass class ViewController : UIViewController { @OverrideInit constructor(coder: NSCoder) : super(coder) @ObjCOutlet lateinit var button: UIButton @ObjCAction fun buttonPressed() { label.text = "Konan says: 'Hello, ${textField.text}!'" } }
swift extends Kotlin class
open class AKotlinClass { open fun helloWorld(greet: String) { ... } }
class SwiftClassInheritedFromKotlinClass : AKotlinClass { override func helloWorld(greet: String) { ... } }
K/N compiles to standard OC framework
import framework into OC/Swift
cinterop
to the rescue
// UIKit.def depends = CFNetwork ... language = Objective-C package = platform.UIKit headers = UIKit/UIKit.h linkerOpts = -framework UIKit ...
cinterop
create Kotlin binding for C and OC/Swift
K/N has already defined cinterop definition files
And you can write your own cinterop definition file, which is easy
fun main() {
println("Hello Kotlin World!")
}
~: kotlinc kmain.kt ~: konanc kmain.kt -o kmain ~: ls -lh total 1680 -rw-r--r-- 1 young staff 687B Nov 26 10:54 KmainKt.class -rwxr-xr-x 1 young staff 829K Nov 26 10:52 kmain.kexe -rw-r--r-- 1 young staff 50B Nov 26 10:50 kmain.kt ~: java KmainKt Hello Kotlin World! ~: ./kmain.kexe Hello Kotlin World!
.
├── SharedCode
│ └── src
│ ├── androidMain
│ ├── androidTest // unit test
│ ├── commonMain
│ ├── commonTest // unit test
│ ├── iOSMain
│ └── iOSTest // unit test
├── android
└── ios
What do you expect? And what I have.
SharedCode
└── src
├── androidMain
│ └── actuals.kt
├── commonMain
│ └── expects.kt
└── iOSMain
└── actuals.kt
Seperate implementation and declaration
Similar to interface, but differs!
commmon-exepcts
expect suspend fun httpGet(url: String): String
anriod-actual
actual suspend fun httpGet(url: String): String = withContext(MyDispatchers.Worker) { val httpConn = URL(url).openConnection() as HttpURLConnection httpConn.connect() httpConn.inputStream.readBytes().toString(Charsets.UTF_8) // ... }
ios-actual
actual suspend fun httpGet(url: String): String { val request = NSMutableURLRequest() request.setHTTPMethod("GET") request.setURL(NSURL(string = url)) // ... }
commmon-exepcts
expect object MyDispatchers { val Main: CoroutineDispatcher }
anriod-actual
actual object MyDispatchers { actual val Main: CoroutineDispatcher = Dispatchers.Main }
ios-actual
actual object MyDispatchers { actual val Main: CoroutineDispatcher = object : CoroutineDispatcher() { override fun dispatch(context: CoroutineContext, block: Runnable) { NSRunLoop.mainRunLoop().performBlock { block.run() } } } }
GOAL
Pros:
- multi-platform
- full kotlin language feature
- easy interop wich OC/Swift/C
- compact runtime and stdlib
- enforce clean architecture
Cons:
- concurrency model is currently mistery
coroutine isn't cuncurrent for now (2018/11/29)